Virus.prototype.nextGeneration = function () {
    let ng = new Grid(this.model.grid().width, this.model.grid().height);
        let y, x;
        for (y = 0; y < this.model.grid().height; y++) {
            for (x = 0; x < this.model.grid().width; x++) {
                if(typeof this.model.cell(x, y) == 'undefined'){
                    continue;
                }
                if(this.model.cell(x, y, 'direct')){
                    this.model.cell(x, y, 'keep', this.model.cell(x, y, 'keep') - 1);
                }else{
                    this.model.cell(x, y, 'direct', random(0,3));
                    this.model.cell(x, y, 'keep', this.config.keepstep);
                }
                loc = fixDirection(this.model.cell(x, y, 'direct'), x, y, this.model.grid().width, this.model.grid().height, this.direct);
                this.Virus(ng, this.model.cell(x, y), loc);
            }
        }
        this.model.grid(ng);
};
Virus.prototype.init = function(){
    this.model.reset();
    this.randomPoint(this.config.color.normal, (this.model.grid().width * this.model.grid().height)/1000, (this.model.grid().width * this.model.grid().height)/100);
    this.randomPoint(this.config.color.risk, 1, 5);
};
Virus.prototype.setSize = function(w, h){
    this.model.setSize(w, h);
};
Virus.prototype.grid = function(){
    return this.model.grid();
};
Virus.prototype.cell = function(x, y, key, value){
    return this.model.cell(x, y, key, value);
};
Virus.prototype.onBind = function(view){

}

function Virus(config){
    this.randomPoint = function(color, min, max){
        let point = random(min,max);
        let i,x,y;
        for(i = 0; i < point; i++){
            x = random(0, this.model.grid().width - 1);
            y = random(0, this.model.grid().height - 1);
            if(typeof this.model.cell(x,y) == 'undefined'){
                this.model.cell(x, y, "keep", this.config.keepstep);
            }
            if(typeof this.model.cell(x, y, "color") !== 'undefined'){
                continue;
            }else{
                this.model.cell(x, y, "color", color);
            }
        }
    };

    this.loadConfig = function(config){
        this.config = {};
        this.config.keepstep = config.keepstep;
        this.config.color = {};
        this.config.color.all = config.g_color;
        this.config.color.risk = config.risk_color;
        this.config.color.ill = config.ill_color;
        this.config.color.normal = config.normal_color;
        this.config.color.doctor = config.doctor_color;
    };

    this.Virus = function(ng, src, loc){
        if(src.keep === 0){
            src.direct = undefined;
            if(src.color == this.config.color.risk && random(0,1)){
                src.color = this.config.color.ill;
            }
        }
        if(typeof ng[loc.dx][loc.dy] != 'undefined'){
            colorset = this.fixColor(src.color, ng[loc.dx][loc.dy].color);
            src.color = this.config.color.all[colorset[0]];
            ng[loc.sx][loc.sy] = src;
            ng[loc.dx][loc.dy].color = this.config.color.all[colorset[1]];
        }else{
            ng[loc.dx][loc.dy] = src;
        }
    };

    this.fixColor = function(src, dst){
        if(src > dst && src < this.config.color.doctor){
            dst++;
        }else if(src > dst && src == this.config.color.doctor && dst > this.config.color.normal){
            dst--;
        }
    
        if(dst > src && dst == this.config.color.doctor && src > this.config.color.normal){
            src--;
        }else if(dst > src && dst < this.config.color.doctor){
            src++;
        }
        return [src, dst];
    };

    this.direct = {
        up          : 0,
        down        : 1,
        left        : 2,
        right       : 3,
    };
    this.model = new Model(config);
    this.loadConfig(config);
}

War.prototype.onBind = function(view){
    view.setDrawGrad(this.drawGrade);
}
War.prototype.nextGeneration = function () {
    // let ng = new Grid(this.model.grid().width, this.model.grid().height);
    if(!this.start){
        let city = this.randomCity(this.getCityCenter());
        this.randomDeploy(this.model.grid().width, this.model.grid().height, city);
        this.start = true;
    }
    let sc,dc,result;
    for(let x = 0; x < this.model.grid().width; x++){
        for(let y = 0; y < this.model.grid().height; y++){
            loc = fixDirection(random(1, 4), x, y, this.model.grid().width, this.model.grid().height, this.direct);
            sc = this.model.cell(loc.sx, loc.sy);
            dc = this.model.cell(loc.dx, loc.dy);
            if(sc == undefined || sc.color == this.config.color.background || sc.color == undefined){
                continue;
            }
            result = this.arbitration(sc, dc);
            for(let key in result.sc){
                this.model.cell(loc.sx, loc.sy, key, result.sc[key]);
            }
            for(let key in result.dc){
                this.model.cell(loc.dx, loc.dy, key, result.dc[key]);
            }
            
        }
    }
    // this.model.grid(ng);
};
War.prototype.init = function(){
    this.start = false;
    this.model.reset();
};
War.prototype.setSize = function(w, h){
    this.model.setSize(w, h);
};
War.prototype.grid = function(){
    return this.model.grid();
};
War.prototype.cell = function(x, y, key, value){
    return this.model.cell(x, y, key, value);
};
War.prototype.grade = {
    mobile      : 1,
    armored     : 2,
    artillery   : 3,
    airforce    : 4,
    base        : 5
};
function War(config){

    this.getCityCenter = function(){
        let num = 0;
        let center = [];
        for(let x = 0; x < this.model.grid().width; x++){
            for(let y = 0; y < this.model.grid().height; y++){
                if(this.model.cell(x, y) != undefined){
                    num++;
                    center.push({x:x, y:y});
                }
            }
        }
        return {
            num: num,
            center: center
        };
    };

    this.randomCity = function(info){
        let total = this.model.grid().width * this.model.grid().height;
        let index = 0;
        let city = {};
        let num = info.num;
        let max;
        for(index; index < num; index++){
            max = Math.floor(total/num);
            city[index] = {};
            city[index].area = random(Math.floor(max/3), max);
            city[index].color = randomColor(city, index);
            city[index].center = info.center[index];
            total -= city[index].area;
            this.model.cell(city[index].center.x, city[index].center.y, "color", city[index].color);
        }
        city.num = num;
        return city;
    };

    this.randomDeploy = function(width, height, city){
        let x, y, loc, num, value, overlay;
        for(let key in this.grade){
            loc = 0;
            value = this.grade[key];
            if(value == this.grade.base){
                continue;
            }
            num = random(city.num, city.num * value);
            while(loc < num){
                x = random(0, width - 1);
                y = random(0, height - 1);
                overlay = false;
                for(let i=0; i < city.num; i++){
                    if(city[i].center.x == x && city[i].center.y == y){
                        overlay = true;
                        break;
                    }
                }
                if(overlay){
                    continue;
                }
                loc++;
                this.model.cell(x, y, "grade", value);
            }
        };
        
    };

    this.drawGrade = (ctx, x, y, width, height, grade)=>{
        ctx.save();
        let radius = Math.floor(width/2) - (this.config.borderWidth * 2);
        if(grade>10 && random(1, width) == radius){
            drawBoom(ctx, x, y, 30, width, height);
            grade -= 10;
        }
        switch(grade){
            case War.prototype.grade.mobile:
                drawArc(ctx, x, y, width, height, this.config.color.background, this.config.color.default, radius);
                drawText(ctx, x, y, width, height, 'MO', this.config.color.background, this.config.color.default);
                break;
            case War.prototype.grade.armored:
                drawArc(ctx, x, y, width, height, this.config.color.background, this.config.color.default, radius);
                drawText(ctx, x, y, width, height, 'AM', this.config.color.background, this.config.color.default);
                break;
            case War.prototype.grade.artillery:
                drawTriangle(ctx, x, y, width, height, 0, this.config.color.background, this.config.color.default);
                drawText(ctx, x, y, width, height, 'TK', this.config.color.background, this.config.color.default);
                break;
            case War.prototype.grade.airforce:
                drawTriangle(ctx, x, y, width, height, 1, this.config.color.background, this.config.color.default);
                drawText(ctx, x, y, width, height, 'AF', this.config.color.background, this.config.color.default);
                break;
            case War.prototype.grade.base:
                drawTriangle(ctx, x, y, width, height, 2, this.config.color.background, this.config.color.default);
                drawText(ctx, x, y, width, height, 'BS', this.config.color.background, this.config.color.default);
                break;
            default:
                break;
        }
        ctx.restore();
    };

    this.join = function(src, dst){
        let sum;
        if(src != undefined && dst != undefined){
            sum = War.prototype.grade.base - src - dst;
            if(sum >= 0){
                dst += src;
                src = undefined;
            }else if(sum < 0){
                dst = War.prototype.grade.base;
                src = Math.abs(sum);
            }
        }else if(src != undefined){
            dst = src;
            src = undefined;
        }
        return {src:src, dst:dst};
    };

    this.counterattack = function(dst){
        return dst - 1 <= 0 ? undefined : dst -1;
    }

    this.attack = function(src, dst){
        let tmp, win;
        if(src != undefined && dst != undefined && src != War.prototype.grade.base){
            tmp = Math.abs(src - dst) + 2;
            if(random(1, tmp) == tmp){
                win = true;
                dst = tmp <= 2 ? undefined:tmp - 2;
                src = src - 1 == 0 ? undefined : src -1; 
            }else if(src < War.prototype.grade.artillery){
                win = false;
                src = undefined;
                dst = this.counterattack(dst) + 10;
            }else{
                win = false;
                src -= 2;
                dst = this.counterattack(dst) + 10;
            }
        }else if(dst == undefined){
            win  = true;
            dst = src;
            src = src > War.prototype.grade.artillery ? War.prototype.grade.mobile:undefined;
        }else{
            win = false;
        }
        return {win:win, src:src, dst:dst};
    };

    this.arbitration = function(atk, dfe){
        let tmp;
        if(dfe == undefined){
            dfe = {};
        }
        if(dfe.color == undefined){
            dfe.color = atk.color;
        }
        if(atk.color == dfe.color){
            tmp = this.join(atk.grade, dfe.grade);
            atk.grade = tmp.src;
            dfe.grade = tmp.dst;
        }else{
            tmp = this.attack(atk.grade, dfe.grade);
            if(tmp.win){
                dfe.color = atk.color;
                dfe.grade = tmp.dst;
                atk.grade = tmp.src;
            }else{
                dfe.grade = tmp.dst;
                atk.grade = tmp.src;
            }
        }
        return {sc:atk, dc:dfe};
    };

    this.model = new Model(config);
    this.config = {};
    this.config.borderWidth = config.border_width;
    this.config.color = {};
    this.config.color.background = config.g_color[config.background_color];
    this.config.color.default = config.g_color[config.default_color];
    this.direct = {
        up          : 1,
        down        : 2,
        left        : 3,
        right       : 4
    };
}